#pragma once
#include <vector>
#include <iostream>
#include <functional>
#include <sys/types.h>
#include <sys/wait.h>
#include "Channel.hpp"
#include "Task.hpp"

// 类似于typedef std::function<void()> work_t，就是给函数对象起别名
using work_t = std::function<void()>;

enum
{
    OK = 0,
    UsageError,
    PipeError,
    ForkError,
};

class ProcessPool
{
public:
    ProcessPool(int n, work_t w) : worker_num(n), work(w)
    {
    }
    ~ProcessPool()
    {
    }
    // 1.初始化进程池
    // channels:输出型参数
    // work:回调函数
    int InitProcessPool()
    {
        // 1.创建指定个数的管道和worker进程
        for (int i = 0; i < worker_num; i++)
        {
            // 1.先有管道
            int pipefd[2] = {0};
            int n = ::pipe(pipefd);
            if (n < 0)
            {
                std::cerr << "pipe error" << std::endl;
                return PipeError;
            }
            // 2.创建worker进程
            pid_t id = ::fork();
            if (id < 0)
            {
                std::cerr << "fork error" << std::endl;
                return ForkError;
            }
            // 3.建立单向通信信道
            else if (id == 0)
            {
                //关闭历史写端fd == 与清空进程池方法的version3相对应
                for(auto &c : channels)
                {
                    c.Close();
                }
                // 子进程：从管道中读取
                ::close(pipefd[1]);
                dup2(pipefd[0], 0); // 让子进程从标准输入中读取命令->将管道的读端重定向到标准输入中
                work();
                ::exit(OK);
            }

            // 父进程：写入管道
            ::close(pipefd[0]);
            channels.emplace_back(pipefd[1], id);
            // Channel ch(pipefd[1], id);
            // channels.push_back(ch);
        }

        return OK;
    }

    // 2.派发任务
    void DispatchTask()
    {
        int who = 0;
        int task_number = 20;
        while (task_number)
        {
            // a.选择一个任务
            int task_code = tm.SelectTask();

            // b.选择一个子进程
            Channel &current_process = channels[who++];
            who %= channels.size();

            std::cout << "###################################" << std::endl;
            std::cout << "send " << task_code << " to " << current_process.Name() << "任务还剩：" << task_number << std::endl;
            std::cout << "###################################" << std::endl;

            // c.派发任务
            current_process.Send(task_code);

            task_number--;
            sleep(1);
        }
    }

    // 3.清空进程池
    void CleanProcessPool()
    {
        // version3:在建立单向通信时就依次关闭历史的写端
        for (auto &c : channels)
        {
            std::string channel_name = c.Name();
            std::cout << "close channel-" << channel_name << " successfully!" << std::endl;
            c.Close();
            pid_t rid = ::waitpid(c.Who(), nullptr, 0);
            if (rid > 0)
            {
                std::cout << "wait child-" << rid << " successfully!" << std::endl;
            }
        }
       

        // version2
        //  for (int i = channels.size()-1; i >= 0; i--)
        //  {
        //      std::string channel_name = channels[i].Name();
        //      std::cout << "close channel-" << channel_name << " successfully!" << std::endl;
        //      channels[i].Close();
        //      pid_t rid = ::waitpid(channels[i].Who(), nullptr, 0);
        //      if (rid > 0)
        //      {
        //          std::cout << "wait child-" << rid << " successfully!" << std::endl;
        //      }
        //  }

        // //version1
        // for (auto &c : channels)
        // {
        //     std::string channel_name = c.Name();
        //     std::cout << "close channel-" << channel_name << " successfully!" << std::endl;
        //     c.Close();
        // }
        // for (auto &c : channels)
        // {
        //     pid_t rid = ::waitpid(c.Who(), nullptr, 0);
        //     if (rid > 0)
        //     {
        //         std::cout << "wait child-" << rid << " successfully!" << std::endl;
        //     }
        // }
    }

    void DebugPrint()
    {
        for (auto &c : channels)
        {
            std::cout << c.Name() << std::endl;
        }
    }

private:
    std::vector<Channel> channels;
    int worker_num;
    work_t work;
};
